package main

import (
    "encoding/json"
    "fmt"
    "github.com/google/uuid"
    "github.com/miekg/dns"
    "net"
    "net/http"
    "os"
    "strings"
    "time"
)

type ZoneEntity struct {
    Id     string `json:"id"`
    Name   string `json:"name,omitempty"`
    Ttl    int    `json:"ttl,omitempty"`
    Zone   string `json:"zone,omitempty"`
    Value  string `json:"value,omitempty"`
    Type   string `json:"type,omitempty"`
    Time   int    `json:"time,omitempty"`
    Status int    `json:"status,omitempty"`
    Mark   string `json:"mark,omitempty"`
}

type DomainEntity struct {
    Id     string `json:"id,omitempty"`
    Name   string `json:"name,omitempty"`
    Mark   string `json:"mark,omitempty"`
    Time   int    `json:"time,omitempty"`
    Status int    `json:"status,omitempty"`
}

type Result struct {
    Code int         `json:"code,omitempty"`
    Msg  string      `json:"msg,omitempty"`
    Data interface{} `json:"data,omitempty"`
}

type LoginIn struct {
    Username string `json:"username"`

    Password string `json:"password"`
}

type LoginOut struct {
    BaseOutDto
    Token string `json:"token,omitempty"`
}

type BaseInDto struct {
    Token string `json:"token,omitempty"`
}
type BaseOutDto struct {
}

func (itDns *ItDns) webSrv() {

    mvc := &Mvc{
        itDns: itDns,
    }

    mvc.webRouter()

    mvc.start()

}

type Mvc struct {
    itDns *ItDns
}

func (mvc *Mvc) webRouter() {

    rs := map[string]func(w http.ResponseWriter, r *http.Request){
        "/":              mvc.indexHandler,
        "/login":         mvc.login,
        "/record/save":   mvc.saveRecord,
        "/record/list":   mvc.queryRecord,
        "/record/delete": mvc.deleteRecord,
        "/record/status": mvc.modifyRecordStatus,
        "/record/mark":   mvc.modifyRecordMark,
        "/domain/list":   mvc.queryDomain,
        "/domain/save":   mvc.saveDomain,
        "/domain/status": mvc.modifyDomainStatus,
        "/domain/delete": mvc.deleteDomain,
        "/domain/find":   mvc.findDomain,
    }

    for u, f := range rs {
        log.Infof("加载路径:%s\n", u)
        http.HandleFunc(u, f)

    }

}
func (mvc *Mvc) start() {

    port := os.Getenv("PORT")

    if port == "" {
        port = "8000"
        log.Infof("Defaulting to port %s", port)
    }

    log.Infof("Listening on port %s", port)
    log.Infof("Open http://localhost:%s in the browser", port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))

}

func (mvc *Mvc) indexHandler(w http.ResponseWriter, r *http.Request) {

    log.Info("==path==>", r.Method, r.URL.Path)

    if r.URL.Path != "/" {
        http.NotFound(w, r)
        return
    }
    _, err := fmt.Fprint(w, "你好,中国!\n你好,ItDns!\n你好,强哥!")
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
    }
}

func ok(data interface{}, w http.ResponseWriter) {

    w.WriteHeader(http.StatusOK)

    res := &Result{
        Code: 200,
        Data: data,
        Msg:  "成功!",
    }

    b, err := json.Marshal(res)

    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Error("JSON结果错误err:", err)
        return
    }

    n, err := w.Write(b)

    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Error("结果错误err:", n, err)
    }
}

func fail(msg string, w http.ResponseWriter) {

    w.WriteHeader(http.StatusOK)

    res := &Result{
        Code: 500,
        Msg:  msg,
    }

    b, err := json.Marshal(res)

    if err != nil {

        w.WriteHeader(http.StatusInternalServerError)

        log.Error("JSON结果错误err:", err)

        return
    }

    n, err := w.Write(b)

    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Error("结果错误err:", n, err)
    }
}

func getBody(r *http.Request, t interface{}) error {

    if r.Method == "POST" {
        return json.NewDecoder(r.Body).Decode(&t)
    }
    return nil
}

func getParam(key string, r *http.Request) string {
    return r.URL.Query().Get(key)
}

//保存记录.
func (mvc *Mvc) saveRecord(w http.ResponseWriter, r *http.Request) {

    log.Info("==保存记录=>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    z := &ZoneEntity{}

    err = json.NewDecoder(r.Body).Decode(&z)

    if err != nil {

        fail(err.Error(), w)

        return
    }

    record := mvc.fmtRecordValue(z)

    if record == nil {

        fail(fmt.Sprintf("不支持解析类型:%s(%d)", z.Type, dns.RcodeNotImplemented), w)

        return
    }

    value, err := json.Marshal(record)

    if z.Id == "" {
        log.Info("插入记录:", z)
        _, err = db.Exec(`INSERT INTO it_dns_records (id,zone, name, ttl, value, type, time, status, mark) VALUES ($1, $2, $3, $4, $5, $6, $7, $8 ,$9)`,
            uuid.New().String(),
            z.Zone,
            z.Name,
            z.Ttl,
            string(value),
            z.Type,
            time.Now().UnixMilli(),
            1,
            "",
        )
    } else {
        log.Info("修改记录:", z)
        _, err = db.Exec(`UPDATE it_dns_records SET zone = $1, name = $2, ttl = $3, value = $4, type = $5, time = $6  WHERE id = $9`,
            z.Zone,
            z.Name,
            z.Ttl,
            string(value),
            z.Type,
            time.Now().UnixMilli(),
            z.Id,
        )
    }

    if err != nil {

        log.Error("===>", err)

        msg := "操作失败!"

        if strings.Contains(err.Error(), "UNIQUE constraint failed") {

            msg = "域名:[" + z.Type + "]" + z.Name + "." + z.Zone + "已经存在!"

        }

        fail(msg, w)

        return
    }

    ok(nil, w)
}

func (mvc *Mvc) getRecordValue(z *ZoneEntity) string {

    value := ""

    var err error

    switch z.Type {

    case "A":
        //将域名指向一个IPV4地址
        record := &ARecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Ip.String()

    case "AAAA":
        //将域名指向一个IPV6地址
        record := &AAAARecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Ip.String()

    case "CNAME":
        //将域名指向另外一个域名
        record := &CNAMERecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Host

    case "SRV":
        //TODO 记录提供特定的服务的服务器

        record := &SRVRecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Target

    case "NS":
        //将子域名指定其他DNS服务器解析
        record := &NSRecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Host

    case "MX":
        //TODO 将域名指向邮件服务器地址
        record := &MXRecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Host
    case "TXT":

        //TODO 文本长度限制512，通常做SPF记录（反垃圾邮件）

        record := &TXTRecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Text

    case "CAA":
        //TODO CA证书颁发机构授权校验显性
        record := &CAARecord{}

        err = json.Unmarshal([]byte(z.Value), &record)

        value = record.Value
    case "URL":

        //{ value: 'URL', label: 'URL    - 将域名重定向到另外一个地址隐性' },
        //{ value: 'URL', label: 'URL    - 与显性URL类似，但是会隐藏真实目标地址' }
    }

    if err != nil {
        log.Error("数据错误:", z.Type, z.Value)
    }

    return value
}

func (mvc *Mvc) fmtRecordValue(z *ZoneEntity) IRecord {
    var record IRecord

    switch z.Type {

    case "A":
        //将域名指向一个IPV4地址
        record = &ARecord{
            Ip: net.ParseIP(z.Value),
        }
    case "AAAA":
        //将域名指向一个IPV6地址
        record = &AAAARecord{
            Ip: net.ParseIP(z.Value),
        }
    case "CNAME":
        //将域名指向另外一个域名
        record = &CNAMERecord{
            Host: z.Value,
        }
    case "SRV":
        //TODO 记录提供特定的服务的服务器
        record = &SRVRecord{
            Target: z.Value,
        }
    case "NS":
        //将子域名指定其他DNS服务器解析
        record = &NSRecord{
            Host: z.Value,
        }
    case "MX":
        //TODO 将域名指向邮件服务器地址
        record = &MXRecord{
            Host: z.Value,
        }
    case "TXT":

        //TODO 文本长度限制512，通常做SPF记录（反垃圾邮件）
        record = &TXTRecord{
            Text: z.Value,
        }

    case "CAA":
        //TODO CA证书颁发机构授权校验显性
        record = &CAARecord{
            Value: z.Value,
        }
    case "URL":

        //{ value: 'URL', label: 'URL    - 将域名重定向到另外一个地址隐性' },
    //{ value: 'URL', label: 'URL    - 与显性URL类似，但是会隐藏真实目标地址' }
    default:

        return nil
    }
    return record
}

//查询解析记录.
func (mvc *Mvc) queryRecord(w http.ResponseWriter, r *http.Request) {

    log.Info("==查询解析记录=>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    row, err := db.Query(` SELECT  id , zone, name, ttl , value  , type, time, status, mark  FROM it_dns_records t `)

    if err != nil {

        fail("查询失败!", w)
        log.Error("查询SQL错误err:", err)
        return
    }

    records := make([]*ZoneEntity, 0)

    for row.Next() {

        z := &ZoneEntity{}

        err = row.Scan(&z.Id, &z.Zone, &z.Name, &z.Ttl, &z.Value, &z.Type, &z.Time, &z.Status, &z.Mark)

        z.Value = mvc.getRecordValue(z)

        if err != nil {

            w.WriteHeader(http.StatusInternalServerError)

            log.Error("查询结果错误err:", err)
        }

        records = append(records, z)
    }

    ok(records, w)

}

//修改解析记录状态.
func (mvc *Mvc) modifyRecordStatus(w http.ResponseWriter, r *http.Request) {

    log.Info("==修改解析记录状态==>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    z := &ZoneEntity{}

    if err := getBody(r, z); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    log.Info("修改记录:", z)
    _, err = db.Exec(`UPDATE it_dns_records SET status = $1  WHERE id = $2`,
        z.Status,
        z.Id,
    )

    if err != nil {
        fail("修改失败:"+err.Error(), w)
        return
    }
    ok("修改成功!", w)
}

//修改解析记录备注.
func (mvc *Mvc) modifyRecordMark(w http.ResponseWriter, r *http.Request) {

    log.Info("==修改解析记录状态==>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    z := &ZoneEntity{}

    if err := getBody(r, z); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    log.Info("修改记录:", z)
    _, err = db.Exec(`UPDATE it_dns_records SET mark = $1  WHERE id = $2`,
        z.Mark,
        z.Id,
    )

    if err != nil {
        fail("修改失败:"+err.Error(), w)
        return
    }
    ok("修改成功!", w)
}

//停用解析记录.
func (mvc *Mvc) disableRecord(w http.ResponseWriter, r *http.Request) {

    log.Info("==path=1=>", r.Method, r.URL.Path)

    z := &ZoneEntity{}

    err := json.NewDecoder(r.Body).Decode(&z)

    log.Info("==body=2=>", z.Name, z.Ttl)

    _, err = fmt.Fprint(w, "你好,中国!", z.Name, z.Ttl)

    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
    }
}

//删除解析记录.
func (mvc *Mvc) deleteRecord(w http.ResponseWriter, r *http.Request) {

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    id := r.URL.Query().Get("id")

    log.Info("=删除解析=>", r.Method, r.URL.Path, id)

    res, err := db.Exec("delete  from it_dns_records where id = $1", id)

    log.Info("删除结果:", res)

    if err != nil {
        fail("删除失败!"+err.Error(), w)
        return
    }

    ok("删除成功!", w)
}

//查询域名.
func (mvc *Mvc) queryDomain(w http.ResponseWriter, r *http.Request) {

    log.Info("==查询解析域名=>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    row, err := db.Query(` SELECT  id ,  name, time, status, mark  FROM it_dns_domain t `)

    if err != nil {

        fail("查询失败!", w)
        log.Error("查询SQL错误err:", err)
        return
    }

    domains := make([]*DomainEntity, 0)

    for row.Next() {

        d := &DomainEntity{}

        err = row.Scan(&d.Id, &d.Name, &d.Time, &d.Status, &d.Mark)

        if err != nil {

            w.WriteHeader(http.StatusInternalServerError)

            log.Error("查询结果错误err:", err)
        }

        domains = append(domains, d)
    }

    ok(domains, w)
}

//查询域名详情.
func (mvc *Mvc) findDomain(w http.ResponseWriter, r *http.Request) {

    log.Info("==path=1=>", r.Method, r.URL.Path)

    z := &ZoneEntity{}

    err := json.NewDecoder(r.Body).Decode(&z)

    log.Info("==body=2=>", z.Name, z.Ttl)

    _, err = fmt.Fprint(w, "你好,中国!", z.Name, z.Ttl)

    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
    }
}

//查询注册域名记录.
func (mvc *Mvc) saveDomain(w http.ResponseWriter, r *http.Request) {

    log.Info("==保存域名=>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    d := &DomainEntity{}

    err = json.NewDecoder(r.Body).Decode(&d)

    if err != nil {

        fail(err.Error(), w)

        return
    }

    if d.Id == "" {
        log.Info("插入域名:", d)
        _, err = db.Exec(`INSERT INTO it_dns_domain (id, name, time, status, mark) VALUES ($1, $2, $3, $4, $5)`,
            uuid.New().String(),
            d.Name,
            time.Now().UnixMilli(),
            1,
            d.Mark,
        )
    } else {
        log.Info("修改域名:", d)
        _, err = db.Exec(`UPDATE it_dns_records SET  name = $1, time = $2 , mark = $3 WHERE id = $9`,
            d.Name,
            time.Now().UnixMilli(),
            d.Mark,
            d.Id,
        )
    }

    if err != nil {

        log.Error("===>", err)

        msg := "操作失败!"

        if strings.Contains(err.Error(), "UNIQUE constraint failed") {

            msg = "域名:[" + d.Name + "]已经存在!"

        }

        fail(msg, w)

        return
    }

    ok(nil, w)
}

//修改域名状态
func (mvc *Mvc) modifyDomainStatus(w http.ResponseWriter, r *http.Request) {

    log.Info("==修改域名状态==>", r.Method, r.URL.Path)

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    d := &DomainEntity{}

    if err := getBody(r, d); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    log.Info("修改记录:", d)
    _, err = db.Exec(`UPDATE it_dns_domain SET status = $1  WHERE id = $2`,
        d.Status,
        d.Id,
    )

    if err != nil {
        fail("修改失败:"+err.Error(), w)
        return
    }
    ok("修改成功!", w)
}

//删除域名
func (mvc *Mvc) deleteDomain(w http.ResponseWriter, r *http.Request) {

    db, err := mvc.itDns.open()

    if err != nil {

        fail(err.Error(), w)

        return
    }

    defer mvc.itDns.close(db)

    id := r.URL.Query().Get("id")

    log.Info("=删除域名=>", r.Method, r.URL.Path, id)

    res, err := db.Exec("delete  from it_dns_domain where id = $1", id)

    log.Info("删除结果:", res)

    if err != nil {
        fail("删除失败!"+err.Error(), w)
        return
    }

    ok("删除成功!", w)
}

//登陆.
func (mvc *Mvc) login(w http.ResponseWriter, r *http.Request) {

    login := &LoginIn{}

    err := getBody(r, login)

    log.Info("登陆信息:", login)

    if err != nil {

        fail("参数错误!", w)

        return
    }

    out := &LoginOut{}

    out.Token = "123"

    ok(out, w)

}
